// ignore_for_file: library_private_types_in_public_api, must_be_immutable

import 'dart:math' as math;

import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_ability/custom_ui.dart';

/// 自定按钮控件
class YJButton extends StatefulWidget {
  YJButton({
    Key? key,
    this.size,
    required this.onPressed,
    this.enabled = true,
    this.onHighlightChanged,
    this.textStyle,
    this.boxShadow,
    this.buttonBoxShadow,
    this.fullWidthButton,
    this.focusColor,
    this.hoverColor,
    this.highlightColor,
    this.splashColor,
    this.elevation = 0.0,
    this.focusElevation = 4.0,
    this.hoverElevation = 4.0,
    this.highlightElevation = 1.0,
    this.disabledElevation = 0.0,
    this.margin,
    this.padding,
    this.constraints,
    this.borderShape,
    this.animationDuration = kThemeChangeDuration,
    this.clipBehavior = Clip.none,
    this.focusNode,
    this.autofocus = false,
    MaterialTapTargetSize? materialTapTargetSize,
    this.child,
    this.type = YJButtonType.solid,
    this.shape = YJButtonShape.standard,
    this.color = YJColors.PRIMARY,
    this.height = 0.0,
    this.textColor,
    this.position = YJPosition.start,
    this.borderSide,
    this.text,
    this.icon,
    this.colorScheme,
    this.enableFeedback,
    this.onLongPress,
    this.disabledColor,
    this.disabledTextColor,
  })  : materialTapTargetSize =
            materialTapTargetSize ?? MaterialTapTargetSize.padded,
        assert(focusElevation >= 0.0),
        assert(hoverElevation >= 0.0),
        assert(highlightElevation >= 0.0),
        assert(disabledElevation >= 0.0),
        super(key: key);

  /// 按钮size类型 默认YJSize.LARGE
  double? size;

  /// 按钮类型
  final YJButtonType type;

  /// 按钮圆角类型
  final YJButtonShape shape;

  /// 背景颜色
  final Color color;

  /// 按钮高 优先于size属性
  final double? height;

  /// 按钮点击事件
  final VoidCallback? onPressed;

  /// 是否激活 true = 激活 ；false = 禁用
  bool enabled;

  /// 按钮文本
  final String? text;

  /// 自定义按钮文字样式
  final TextStyle? textStyle;

  /// 按钮文字颜色
  final Color? textColor;

  ///  禁用时 按钮颜色
  final Color? disabledColor;

  ///  禁用时 文本颜色
  final Color? disabledTextColor;

  /// 是否屏宽
  final bool? fullWidthButton;

  final ValueChanged<bool>? onHighlightChanged;

  final BorderSide? borderSide;

  final BoxShadow? boxShadow;

  /// 焦点颜色
  final Color? focusColor;

  /// 悬停时按钮背景颜色
  final Color? hoverColor;

  ///  按住不放时的颜色
  final Color? highlightColor;

  /// InkWell 溅墨颜色
  final Color? splashColor;

  final double elevation;

  final double hoverElevation;

  final double focusElevation;

  final double highlightElevation;

  final double disabledElevation;

  final EdgeInsetsGeometry? margin;

  final EdgeInsetsGeometry? padding;

  final BoxConstraints? constraints;

  final ShapeBorder? borderShape;

  final Duration animationDuration;

  /// 当不显示text内容时控件的子widget
  final Widget? child;

  final MaterialTapTargetSize materialTapTargetSize;

  final FocusNode? focusNode;

  final bool autofocus;

  final Clip clipBehavior;

  /// 图标icon
  final Widget? icon;

  /// 图标显示位置，默认在右
  final YJPosition position;

  final bool? buttonBoxShadow;

  final ColorScheme? colorScheme;

  final bool? enableFeedback;

  final VoidCallback? onLongPress;

  @override
  _YJButtonState createState() => _YJButtonState();
}

class _YJButtonState extends State<YJButton> {
  late Color color;
  Color? textColor;
  Color? disabledColor;
  Color? disabledTextColor;
  Widget? child;
  Widget? icon;
  Function? onPressed;
  late YJButtonType type;
  late YJButtonShape shape;
  late YJPosition position;
  late BoxShadow boxShadow;

  final Set<MaterialState> _states = <MaterialState>{};

  @override
  void initState() {
    widget.size = widget.size ?? YJSize.LARGE;
    color = widget.color;
    textColor = widget.textColor;
    child = widget.text != null ? Text(widget.text!) : widget.child;
    icon = widget.icon;
    onPressed = widget.onPressed;
    type = widget.type;
    shape = widget.shape;
    position = widget.position;
    disabledColor = widget.disabledColor;
    disabledTextColor = widget.disabledTextColor;
    _updateState(
      MaterialState.disabled,
      !widget.enabled,
    );
    super.initState();
  }

  bool get _hovered => _states.contains(MaterialState.hovered);

  bool get _focused => _states.contains(MaterialState.focused);

  bool get _pressed => _states.contains(MaterialState.pressed);

  bool get _disabled => _states.contains(MaterialState.disabled);

  void _updateState(MaterialState state, bool value) {
    value ? _states.add(state) : _states.remove(state);
  }

  void _handleHighlightChanged(bool value) {
    if (_pressed != value) {
      setState(() {
        _updateState(MaterialState.pressed, value);
        if (widget.onHighlightChanged != null) {
          widget.onHighlightChanged!(value);
        }
      });
    }
  }

  void _handleHoveredChanged(bool value) {
    if (_hovered != value) {
      setState(() {
        _updateState(MaterialState.hovered, value);
      });
    }
  }

  void _handleFocusedChanged(bool value) {
    if (_focused != value) {
      setState(() {
        _updateState(MaterialState.focused, value);
      });
    }
  }

  @override
  void didUpdateWidget(YJButton oldWidget) {
    _updateState(MaterialState.disabled, !widget.enabled);
    if (_disabled && _pressed) {
      _handleHighlightChanged(false);
    }
    color = widget.color;
    textColor = widget.textColor;
    child = widget.text != null ? Text(widget.text!) : widget.child;
    icon = widget.icon;
    onPressed = widget.onPressed;
    type = widget.type;
    shape = widget.shape;
    position = widget.position;
    disabledColor = widget.disabledColor;
    disabledTextColor = widget.disabledTextColor;
    _updateState(
      MaterialState.disabled,
      !widget.enabled,
    );
    super.didUpdateWidget(oldWidget);
  }

  double get _effectiveElevation {
    if (_disabled) {
      return widget.disabledElevation;
    }
    if (_pressed) {
      return widget.highlightElevation;
    }
    if (_hovered) {
      return widget.hoverElevation;
    }
    if (_focused) {
      return widget.focusElevation;
    }
    return widget.elevation;
  }

  @override
  Widget build(BuildContext context) {
    ShapeBorder shapeBorderType;

    /// 按钮高度
    getHeight() {
      if (widget.height == null || widget.height == 0) {
        return widget.size ?? YJSize.LARGE;
      }
      return widget.height!;
    }

    /// 按钮宽度
    double? buttonWidth() {
      double? buttonWidth = 0;
      if (widget.fullWidthButton == true) {
        buttonWidth = MediaQuery.of(context).size.width;
      } else {
        buttonWidth = null;
      }
      return buttonWidth;
    }

    /// 内边距
    getPadding() {
      return widget.padding != null
          ? widget.padding
          : EdgeInsets.symmetric(horizontal: YJSize.LAYOUT_PADDING_16);
    }

    /// 外边距
    getMargin() {
      return widget.margin != null
          ? widget.margin
          : EdgeInsets.symmetric(vertical: YJSize.LAYOUT_PADDING_4);
    }

    Color getBorderColor() {
      if (widget.enabled) {
        final Color fillColor = color;
        return fillColor;
      } else {
        if (disabledColor != null) {
          return disabledColor!;
        } else {
          return color.withOpacity(0.38);
        }
      }
    }

    Color getDisabledFillColor() {
      if (widget.type == YJButtonType.transparent ||
          widget.type == YJButtonType.outline ||
          widget.type == YJButtonType.outline2x) {
        return Colors.transparent;
      }
      if (disabledColor != null) {
        return disabledColor!;
      } else {
        return color.withOpacity(0.38);
      }
    }

    Color getColor() {
      if (widget.type == YJButtonType.transparent ||
          widget.type == YJButtonType.outline ||
          widget.type == YJButtonType.outline2x) {
        return Colors.transparent;
      }
      final Color fillColor = color;
      return fillColor;
    }

    Color getDisabledTextColor() {
      if (disabledTextColor != null) {
        return disabledTextColor!;
      } else if (widget.type == YJButtonType.outline ||
          widget.type == YJButtonType.outline2x ||
          widget.type == YJButtonType.transparent) {
        return color;
      } else {
        return YJColors.BLACK;
      }
    }

    Color getTextColor() {
      if (widget.type == YJButtonType.outline ||
          widget.type == YJButtonType.outline2x ||
          widget.type == YJButtonType.transparent) {
        return widget.enabled
            ? textColor == null
                ? color == YJColors.TRANSPARENT
                    ? YJColors.BLACK
                    : color
                : textColor!
            : getDisabledTextColor();
      }
      if (textColor == null) {
        if (color == YJColors.TRANSPARENT) {
          return YJColors.BLACK;
        } else {
          return YJColors.WHITE;
        }
      } else {
        return textColor!;
      }
    }

    final Color themeColor =
        Theme.of(context).colorScheme.onSurface.withOpacity(0.12);
    final BorderSide outlineBorder = BorderSide(
      color: widget.borderSide == null
          ? getBorderColor()
          : widget.borderSide!.color,
      width: (widget.borderSide?.width ??
          (widget.type == YJButtonType.outline2x ? 2.0 : 1.0)),
    );

    Size minSize;
    switch (widget.materialTapTargetSize) {
      case MaterialTapTargetSize.padded:
        minSize = Size.zero;
        // const Size(48, 48);
        break;
      case MaterialTapTargetSize.shrinkWrap:
        minSize = Size.zero;
        break;
      default:
        minSize = Size.zero;
        break;
    }

    final BorderSide shapeBorder = widget.type == YJButtonType.outline ||
            widget.type == YJButtonType.outline2x
        ? outlineBorder
        : widget.borderSide ??
            BorderSide(
              color: getBorderColor(),
              width: 0,
            );

    if (shape == YJButtonShape.pills) {
      shapeBorderType = RoundedRectangleBorder(
        borderRadius: BorderRadius.circular(
          50,
        ),
        side: shapeBorder,
      );
    } else if (shape == YJButtonShape.square) {
      shapeBorderType = RoundedRectangleBorder(
        borderRadius: BorderRadius.circular(0),
        side: shapeBorder,
      );
    } else if (shape == YJButtonShape.standard) {
      shapeBorderType = RoundedRectangleBorder(
        borderRadius: BorderRadius.circular(4),
        side: shapeBorder,
      );
    } else {
      shapeBorderType = RoundedRectangleBorder(
        borderRadius: BorderRadius.circular(50),
        side: shapeBorder,
      );
    }

    BoxDecoration? getBoxShadow() {
      if (widget.type != YJButtonType.transparent) {
        if (widget.boxShadow == null && widget.buttonBoxShadow != true) {
          return null;
        } else {
          return BoxDecoration(
            color: widget.type == YJButtonType.transparent ||
                    widget.type == YJButtonType.outline ||
                    widget.type == YJButtonType.outline2x
                ? Colors.transparent
                : color,
            borderRadius: widget.shape == YJButtonShape.pills
                ? BorderRadius.circular(50)
                : widget.shape == YJButtonShape.standard
                    ? BorderRadius.circular(5)
                    : BorderRadius.zero,
            boxShadow: [
              widget.boxShadow == null && widget.buttonBoxShadow == true
                  ? BoxShadow(
                      color: themeColor,
                      blurRadius: 1.5,
                      spreadRadius: 2,
                      offset: Offset.zero,
                    )
                  : widget.boxShadow != null
                      ? widget.boxShadow!
                      : BoxShadow(
                          color: Theme.of(context).canvasColor,
                          blurRadius: 0,
                          spreadRadius: 0,
                          offset: Offset.zero,
                        ),
            ],
          );
        }
      }
      return null;
    }

    TextStyle getTextStyle() {
      if (widget.size == YJSize.SMALL) {
        return TextStyle(
          color: widget.enabled ? getTextColor() : getDisabledTextColor(),
          fontSize: YJSize.dp(14),
        );
      } else if (widget.size == YJSize.MEDIUM) {
        return TextStyle(
          color: widget.enabled ? getTextColor() : getDisabledTextColor(),
          fontSize: YJSize.dp(14),
          fontWeight: FontWeight.w400,
        );
      } else if (widget.size == YJSize.LARGE) {
        return TextStyle(
          color: widget.enabled ? getTextColor() : getDisabledTextColor(),
          fontSize: YJSize.dp(16),
          fontWeight: FontWeight.w500,
        );
      }
      return TextStyle(
        color: widget.enabled ? getTextColor() : getDisabledTextColor(),
        fontSize: YJSize.dp(16),
        fontWeight: FontWeight.w500,
      );
    }

    final Widget result = Container(
      height: getHeight(),
      width: buttonWidth(),
      margin: getMargin(),
      constraints: icon == null
          ? const BoxConstraints(minWidth: 28)
          : const BoxConstraints(minWidth: 44),
      decoration: widget.type == YJButtonType.solid ? getBoxShadow() : null,
      child: Material(
        elevation: _effectiveElevation,
        textStyle: widget.textStyle ?? getTextStyle(),
        shape: widget.type == YJButtonType.transparent
            ? null
            : widget.borderShape ?? shapeBorderType,
        color: widget.enabled ? getColor() : getDisabledFillColor(),
        type: MaterialType.button,
        animationDuration: widget.animationDuration,
        clipBehavior: widget.clipBehavior,
        child: InkWell(
          focusNode: widget.focusNode,
          canRequestFocus: widget.enabled,
          onFocusChange: _handleFocusedChanged,
          autofocus: widget.autofocus,
          onHighlightChanged: _handleHighlightChanged,
          onHover: _handleHoveredChanged,
          onTap: widget.enabled == true ? widget.onPressed : null,
          onLongPress: widget.onLongPress,
          enableFeedback: widget.enableFeedback,
          splashColor: widget.splashColor,
          highlightColor: widget.highlightColor,
          focusColor: widget.focusColor,
          hoverColor: widget.hoverColor,
          customBorder: widget.type == YJButtonType.transparent
              ? null
              : widget.borderShape ?? shapeBorderType,
          child: Container(
            padding: getPadding(),
            child: Center(
              widthFactor: 1,
              heightFactor: 1,
              child: icon != null &&
                      child != null &&
                      (position == YJPosition.start)
                  ? Row(
                      mainAxisSize: MainAxisSize.min,
                      children: <Widget>[
                        icon!,
                        SizedBox(width: YJSize.LAYOUT_PADDING_4),
                        child!
                      ],
                    )
                  : icon != null &&
                          child != null &&
                          (position == YJPosition.end)
                      ? Row(
                          mainAxisSize: MainAxisSize.min,
                          children: <Widget>[
                            child!,
                            SizedBox(width: YJSize.LAYOUT_PADDING_4),
                            icon!
                          ],
                        )
                      : child,
            ),
          ),
        ),
      ),
    );

    return Semantics(
      container: true,
      button: true,
      enabled: widget.enabled,
      child: _InputPadding(
        minSize: minSize,
        child: Focus(
          focusNode: widget.focusNode,
          onFocusChange: _handleFocusedChanged,
          autofocus: widget.autofocus,
          child: result,
        ),
      ),
    );
  }
}

class _InputPadding extends SingleChildRenderObjectWidget {
  const _InputPadding({
    Key? key,
    Widget? child,
    this.minSize,
  }) : super(
          key: key,
          child: child,
        );

  final Size? minSize;

  @override
  RenderObject createRenderObject(BuildContext context) =>
      _RenderInputPadding(minSize);

  @override
  void updateRenderObject(
      BuildContext context, covariant _RenderInputPadding renderObject) {
    renderObject.minSize = minSize;
  }
}

class _RenderInputPadding extends RenderShiftedBox {
  _RenderInputPadding(this._minSize, [RenderBox? child]) : super(child);

  Size? get minSize => _minSize;
  Size? _minSize;

  set minSize(Size? value) {
    _minSize = value;
    markNeedsLayout();
  }

  @override
  double computeMinIntrinsicWidth(double height) {
    if (child != null && minSize != null) {
      return math.max(child!.getMinIntrinsicWidth(height), minSize!.width);
    }
    return 0;
  }

  @override
  double computeMinIntrinsicHeight(double width) {
    if (child != null && minSize != null) {
      return math.max(child!.getMinIntrinsicHeight(width), minSize!.height);
    }
    return 0;
  }

  @override
  double computeMaxIntrinsicWidth(double height) {
    if (child != null && minSize != null) {
      return math.max(child!.getMaxIntrinsicWidth(height), minSize!.width);
    }
    return 0;
  }

  @override
  double computeMaxIntrinsicHeight(double width) {
    if (child != null && minSize != null) {
      return math.max(child!.getMaxIntrinsicHeight(width), minSize!.height);
    }
    return 0;
  }

  @override
  void performLayout() {
    if (child != null && minSize != null) {
      child!.layout(constraints, parentUsesSize: true);
      // ignore: avoid_as
      final BoxParentData childParentData = child!.parentData as BoxParentData;
      final double height = math.max(child!.size.width, minSize!.width);
      final double width = math.max(child!.size.height, minSize!.height);
      size = constraints.constrain(Size(height, width));
      childParentData.offset =
          // ignore: avoid_as
          Alignment.center.alongOffset(size - child!.size as Offset);
    } else {
      size = Size.zero;
    }
  }

  @override
  bool hitTest(BoxHitTestResult result, {required Offset position}) {
    if (super.hitTest(result, position: position)) {
      return true;
    }

    if (child != null) {
      final Offset center = child!.size.center(Offset.zero);
      return result.addWithRawTransform(
        transform: MatrixUtils.forceToPoint(center),
        position: center,
        hitTest: (BoxHitTestResult result, Offset position) {
          assert(position == center);
          return child!.hitTest(
            result,
            position: center,
          );
        },
      );
    }
    throw Exception('child property cannot be null');
  }
}
